home *** CD-ROM | disk | FTP | other *** search
- /* SCSIVBLSample.c */
- /*
- * SCSIVBLSample.c
- * Copyright © 1994 Apple Computer Inc. All rights reserved.
- * This is a minimal sample to illustrate calling the SCSI Manager from a
- * VBL task. It displays drive ready/not ready status changes.
- *
- * This program requires SCSI Manager 4.3.
- */
- #include <Errors.h>
- #include <Script.h>
- #include <Types.h>
- #include <Files.h>
- #include <Resources.h>
- #include <QuickDraw.h>
- #include <Fonts.h>
- #include <Events.h>
- #include <Windows.h>
- #include <ToolUtils.h>
- #include <Memory.h>
- #include <Menus.h>
- #include <Lists.h>
- #include <Printing.h>
- #include <Dialogs.h>
- #include <StandardFile.h>
- #include <Packages.h>
- #include <Retrace.h>
-
- #include "SCSIVBLSample.h"
-
- #ifndef FALSE
- #define FALSE 0
- #define TRUE 1
- #endif
-
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * As of this writing, the Asynchronous SCSI Manager definitions have not
- * been added to the standard header distribution. This include references
- * a working version that is stored in the application folder hierarchy.
- *
- * Include the O.S. files in a specific order to make sure that we have
- * a definition for the _SCSIAtomic trap.
- */
- #include <Traps.h>
- #ifndef _SCSIAtomic
- #define _SCSIAtomic 0xA089
- #endif
- #include "SCSI.h"
-
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * Define the device that we will use. Note that the orignnal SCSI Manager
- * only uses kSCSITargetID, while the asynchronous SCSI Manager also uses
- * the bus and LUN data. You must set this for your particular system.
- * configuration. (Remember, this is a test.)
- */
- #ifndef kSCSIBusID
- #define kSCSIBusID 1 /* Quadra 950 external bus */
- #endif
- #ifndef kSCSITargetID
- #define kSCSITargetID 4 /* Specific to my P8100 (a CD device) */
- //#define kSCSITargetID 2 /* Specific to my Q950 (a CD device) */
- #endif
- #ifndef kSCSILUN
- #define kSCSILUN 0 /* A pretty safe choice */
- #endif
-
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * SCSI Definitions
- * We will issuing a Device Inquiry to the device. In a real application,
- * we would probably just issue a Test Unit Ready, but this code was done
- * to try to duplicate a developer problem, so we're doing the operation
- * that they had a problem with. Upon debugging, I discovered that the
- * particular device I'm working with did not return Check Condition
- * if Device Inquiry was issued and no CD was inserted. Thus, we switch
- * between two commands.
- */
- #define kScsiCmdTestUnitReady 0x00 /* Test unit ready command */
- #define kScsiCmdInquiry 0x12 /* Device inquiry command */
-
- struct SCSI_6_Byte_Command { /* Six-byte command */
- unsigned char opcode; /* 0 */
- unsigned char lbn3; /* 1 lbn in low 5 */
- unsigned char lbn2; /* 2 */
- unsigned char lbn1; /* 3 */
- unsigned char len; /* 4 */
- unsigned char ctrl; /* 5 */
- };
- typedef struct SCSI_6_Byte_Command SCSI_6_Byte_Command;
-
- struct SCSI_Inquiry_Data { /* Inquiry returns this */
- unsigned char devType; /* 0 Device type, */
- unsigned char devTypeMod; /* 1 Device type modifier */
- unsigned char version; /* 2 ISO/ECMA/ANSI version */
- unsigned char format; /* 3 Response data format */
- unsigned char length; /* 4 Additional Length */
- unsigned char reserved5; /* 5 Reserved */
- unsigned char reserved6; /* 6 Reserved */
- unsigned char flags; /* 7 Capability flags */
- unsigned char vendor[8]; /* 8-15 Vendor-specific */
- unsigned char product[16]; /* 16-31 Product id */
- unsigned char revision[4]; /* 32-35 Product revision */
- unsigned char vendorSpecific[20]; /* 36-55 Vendor stuff */
- unsigned char moreReserved[40]; /* 56-95 Reserved */
- };
- typedef struct SCSI_Inquiry_Data SCSI_Inquiry_Data;
-
- struct SCSI_Sense_Data { /* Request Sense result */
- unsigned char errorCode; /* 0 Class code, valid lbn */
- unsigned char segmentNumber; /* 1 Segment number */
- unsigned char senseKey; /* 2 Sense key and flags */
- unsigned char info[4];
- unsigned char additionalSenseLength;
- unsigned char reservedForCopy[4];
- unsigned char additionalSenseCode;
- unsigned char additionalSenseQualifier;
- unsigned char fruCode; /* Field replacable unit code */
- unsigned char senseKeySpecific[2];
- unsigned char additional[101];
- };
- typedef struct SCSI_Sense_Data SCSI_Sense_Data;
- /*
- * The high-bit of errorCode signals whether there is a logical
- * block. The low value signals whether there is a valid sense
- */
- #define kScsiSenseHasLBN 0x80 /* Logical block number set */
- #define kScsiSenseInfoValid 0x70 /* Is sense key valid? */
- #define kScsiSenseInfoMask 0x70 /* Mask for sense info */
- /*
- * These bits may be set in the sense key
- */
- #define kScsiSenseKeyMask 0x0F
- #define kScsiSenseILI 0x20 /* Illegal logical Length */
- #define kScsiSenseEOM 0x40 /* End of media */
- #define kScsiSenseFileMark 0x80 /* End of file mark */
-
- /*
- * SCSI sense codes. (Returned after request sense).
- */
- #define kScsiSenseNone 0x00 /* No error */
- #define kScsiSenseRecoveredErr 0x01 /* Warning */
- #define kScsiSenseNotReady 0x02 /* Device not ready */
- #define kScsiSenseMediumErr 0x03 /* Device medium error */
- #define kScsiSenseHardwareErr 0x04 /* Device hardware error */
- #define kScsiSenseIllegalReq 0x05 /* Illegal request for dev. */
- #define kScsiSenseUnitAtn 0x06 /* Unit attention (not err) */
- #define kScsiSenseDataProtect 0x07 /* Data protection */
- #define kScsiSenseBlankCheck 0x08 /* Tape-specific error */
- #define kScsiSenseVendorSpecific 0x09 /* Vendor-specific error */
- #define kScsiSenseCopyAborted 0x0a /* Copy request cancelled */
- #define kScsiSenseAbortedCmd 0x0b /* Initiator aborted cmd. */
- #define kScsiSenseEqual 0x0c /* Comparison equal */
- #define kScsiSenseVolumeOverflow 0x0d /* Write past end mark */
- #define kScsiSenseMiscompare 0x0e /* Comparison failed */
- #define kScsiSenseCurrentErr 0x70
- #define kScsiSenseDeferredErr 0x71
-
- /*
- * We switch between these two commands:
- */
- #define kIssueDeviceInquiry 0 /* Must be zero */
- #define kIssueTestUnitReady 1 /* Must be one */
-
- /*
- * This will select the correct message to display in the alert.
- */
- typedef enum { /* Device rediness state */
- kDeviceInitialState = 0, /* Initializing, must be zero */
- kDeviceNotPresent, /* No device at this bus id */
- kDeviceNotReady, /* TestUnitReady/CheckCondition */
- kDeviceReady /* TestUnitReady/statusGood */
- } DeviceState;
-
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * This is the VBL task structure that we need. All data is allocated on
- * the System Heap to avoid problems with virtual memory. (But, note that
- * the VBL task is in the application heap, so you should never run this
- * test with virtual memory enabled).
- *
- * This extended VBL structure has all of the data that the VBL and
- * SCSI command needs.
- *
- * Operational state is defined by the following variables. They are
- * actually application-specific globals, but are defined as elements of
- * the ExtendedVBLTask record to eliminate the need to reference A5 from
- * the VBL and SCSI completion routine. This simplifies extension of this
- * code to support multiple devices. It would be better to combine these
- * variables into a coherent finite-state automaton.
- *
- * deviceState Set by the SCSI Manager to one of the three
- * "active" states: not present, not ready, or
- * ready. If this differs from oldDeviceState,
- * the application event loop will post an alert.
- * Set by: SCSI Completion,
- * Read by: Application event loop
- * oldDeviceState The state that the application knows about.
- * Note: in the real world, we might use a
- * message buffer held in an OS Queue to avoid
- * any possible race conditions.
- * Set by: Application Event loop
- * Read by: Application Event loop
- * quitNow Set by the application when the user hits
- * a key or clicks the "Exit" button. Read by
- * the VBL. If set, the VBL does not post an I/O
- * request and does not post a VBL timer request.
- * Set by: Application Event Loop
- * Read by: VBL Task
- * vblActive Set by the application when it installs the VBL
- * at startup. Cleared by the VBL when it decides
- * not to re-install itself, either because of a
- * serious error (SCSI Manager error) or because
- * the user quit and VBL.quitNow is TRUE. The
- * application continues processing events while
- * vblActive is TRUE; it exits when vblActive and
- * scsiActive are both FALSE.
- * Set by: Application startup
- * Cleared by: VBL on exit
- * Read by Application Event Loop
- * scsiActive Set by the VBL when it starts an asynchronous
- * I/O request, cleared by the completion routine.
- * Set by: VBL on issuing SCSI I/O request
- * Cleared by: SCSI Completion routine
- * Read by: VBL to determine whether to issue another request,
- * Read by: Application Event Loop on exit.
- *
- * Overall operational state (ignoring errors):
- * 0. All flags set FALSE.
- * 1. Create the VBL and SCSI parameter block, install the VBL, set
- * gVBLActive, and enter the event loop. Note that there is a
- * potential race condition if the first VBL fails. Work around
- * this by installing the VBL with a longer time delay.
- * 2. (Mainline): process events until any of the following occur:
- * deviceState != oldDeviceState:
- * Copy deviceState to oldDeviceState and post an
- * Alert with the new device state. Set quitNow if
- * the device is non-existant.
- * vblActive and scsiActive are both FALSE: dispose of the VBL
- * and SCSI parameter blocks. ExitToShell.
- */
- typedef struct {
- VBLTask vblTask; /* VBL Manager data */
- long applicationA5; /* To access strings etc. */
- unsigned long vblCount; /* VBL interrupts */
- unsigned long scsiRequestCount; /* SCSI requests issued */
- unsigned long scsiCompleteCount; /* SCSI requests completed */
- unsigned long scsiWasBusy; /* SCSI was busy in VBL */
- unsigned long eventLoopCount; /* WaitNextEvent cycles */
- unsigned long scsiExecIOPBSize; /* SCSI pb length */
- SCSIExecIOPB *scsiPBPtr; /* For new SCSI only */
- SCSI_Inquiry_Data inquiryData; /* Returned from the device */
- SCSI_Sense_Data senseData; /* Autosense information */
- short commandSwitch; /* Test Ready or Inquiry? */
- Boolean quitNow; /* TRUE to stop the VBL */
- Boolean vblActive; /* TRUE when VBL is running */
- Boolean scsiActive; /* TRUE during async I/O */
- DeviceState deviceState; /* Drive Inquiry result */
- DeviceState oldDeviceState; /* Event Manager's value */
- OSErr scsiError; /* SCSI Manager failure */
- } ExtendedVBLTask, *ExtendedVBLTaskPtr;
- #define VBL (*extendedVBLTaskPtr)
- #define SCSI (*VBL.scsiPBPtr)
-
- ExtendedVBLTaskPtr gExtendedVBLTaskPtr; /* The VBL "globals" */
- #define kCompletionTimeout (250) /* 250 Msec total time */
-
- StringPtr gDeviceStateString[] = {
- "\pVBL initial state",
- "\pDevice not present - cannot continue",
- "\pDevice not ready, waiting for device ready",
- "\pDevice ready"
- };
-
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * Local functions and administrative variables.
- */
-
- #ifdef __powerc
- QDGlobals qd;
- #endif
-
- void DisplayFinishedMessage(
- register ExtendedVBLTaskPtr extendedVBLTaskPtr
- );
- Boolean DisplayError(
- OSErr errorStatus,
- ConstStr255Param errorMessage
- );
- void DisplaySCSIInquiry(void);
- extern Boolean AsyncSCSIPresent(void);
-
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * VBL and SCSI functions.
- */
- ExtendedVBLTaskPtr CreateVBLTask(void);
- void AllocateSCSIParamBlock(
- register ExtendedVBLTaskPtr extendedVBLTaskPtr
- );
- void InitializeSCSIParamBlock(
- register ExtendedVBLTaskPtr extendedVBLTaskPtr
- );
- void InstallVBLTask(
- register ExtendedVBLTaskPtr extendedVBLTaskPtr
- );
- pascal void MySCSICallbackProc(
- SCSIExecIOPB *scsiExecIOPBPtr
- );
-
- #ifdef __powerc
- pascal void MyVBLTask(
- VBLTaskPtr vblTaskPtr
- );
- #else
- pascal void MyVBLTask(void);
- unsigned long GetA0(void) = { 0x2008 };
- #endif
-
- /*
- * Cheap 'n dirty memory clear routine.
- */
- #define CLEAR(record) do { \
- register char *ptr = (char *) &record; \
- register long size; \
- for (size = sizeof record; size > 0; --size) \
- *ptr++ = 0; \
- } while (0)
-
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * Event-loop mainline.
- */
- void
- main(void)
- {
- EventRecord eventRecord;
- register ExtendedVBLTaskPtr extendedVBLTaskPtr;
-
- MaxApplZone();
- InitGraf(&qd.thePort);
- InitFonts();
- InitWindows();
- InitMenus();
- TEInit();
- InitDialogs(0);
- if (AsyncSCSIPresent() == FALSE) {
- InitCursor();
- StopAlert(ALRT_NoNewSCSI, NULL);
- ExitToShell();
- }
- SetCursor(*GetCursor(watchCursor));
- gExtendedVBLTaskPtr = extendedVBLTaskPtr = CreateVBLTask();
- /*
- * These functions ExitToShell on failure.
- */
- AllocateSCSIParamBlock(extendedVBLTaskPtr);
- InstallVBLTask(extendedVBLTaskPtr);
- /*
- * Run the event loop
- */
- while (VBL.vblActive || VBL.scsiActive) {
- WaitNextEvent(everyEvent, &eventRecord, 6, NULL);
- ++VBL.eventLoopCount;
- if (VBL.deviceState != VBL.oldDeviceState) {
- VBL.oldDeviceState = VBL.deviceState;
- ParamText(
- gDeviceStateString[VBL.oldDeviceState],
- "\p", "\p", "\p"
- );
- InitCursor();
- if (NoteAlert(ALRT_Info, NULL) == cancel
- || VBL.oldDeviceState == kDeviceNotPresent)
- VBL.quitNow = TRUE;
- SetCursor(*GetCursor(watchCursor));
- }
- if (VBL.scsiError != noErr) {
- VBL.quitNow |= DisplayError(
- VBL.scsiError,
- "\pAsynchronous SCSI error"
- );
- if (VBL.quitNow == FALSE)
- VBL.scsiError = noErr;
- SetCursor(*GetCursor(watchCursor));
- }
- /*
- * MouseDown doesn't work as there is no window to
- * mouse into.
- */
- if (eventRecord.what == keyDown)
- VBL.quitNow = TRUE;
- }
- DisplayFinishedMessage(extendedVBLTaskPtr);
- DisposePtr((Ptr) extendedVBLTaskPtr->scsiPBPtr);
- DisposePtr((Ptr) extendedVBLTaskPtr);
- ExitToShell();
- }
-
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * Display a message when we're done.
- */
- void
- DisplayFinishedMessage(
- register ExtendedVBLTaskPtr extendedVBLTaskPtr
- )
- {
- Str15 vblCountMsg;
- Str15 scsiCompleteCountMsg;
- Str15 scsiWasBusyMsg;
- Str15 eventLoopCountMsg;
-
- NumToString(VBL.vblCount, vblCountMsg);
- NumToString(VBL.scsiCompleteCount, scsiCompleteCountMsg);
- NumToString(VBL.scsiWasBusy, scsiWasBusyMsg);
- NumToString(VBL.eventLoopCount, eventLoopCountMsg);
- ParamText(
- vblCountMsg,
- scsiCompleteCountMsg,
- scsiWasBusyMsg,
- eventLoopCountMsg
- );
- InitCursor();
- NoteAlert(ALRT_Finished, NULL);
- }
-
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * Display an error alert. Return TRUE if the user clicks "Exit"
- */
- Boolean
- DisplayError(
- OSErr errorStatus,
- ConstStr255Param errorMessage
- )
- {
- Str255 work;
-
- if (errorStatus == noErr)
- ParamText("\p(no error)", errorMessage, "\p", "\p");
- else {
- NumToString(errorStatus, work);
- ParamText(work, errorMessage, "\p", "\p");
- }
- InitCursor();
- return (StopAlert(ALRT_Error, NULL) == cancel);
- }
-
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * The following section contains all VBL and SCSI-specific code.
- * Build the VBL "global" area. Called once during application startup.
- * Note: under MetroWerks and Think, be sure to add saving and restoring
- * register A4 if you incorporate this into a driver or other
- * non-application code segment.
- */
- ExtendedVBLTaskPtr
- CreateVBLTask(void)
- {
- register ExtendedVBLTaskPtr extendedVBLTaskPtr;
-
- extendedVBLTaskPtr = (ExtendedVBLTaskPtr)
- NewPtrSysClear(sizeof (ExtendedVBLTask));
- if (extendedVBLTaskPtr == NULL) {
- DisplayError(MemError(), "\pCan't create extended VBL task");
- ExitToShell();
- }
- VBL.applicationA5 = SetCurrentA5(); /* Link to appl. globals */
- VBL.vblTask.qType = vType; /* VBL queue type */
- VBL.vblTask.vblAddr = NewVBLProc(MyVBLTask);
- VBL.vblTask.vblCount = 60; /* Initial long sleep */
- return (extendedVBLTaskPtr);
- }
-
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * Call the SCSIBusInquiry command to determine the length of the command
- * block for the actual transfer. Called once during application startup.
- */
- void
- AllocateSCSIParamBlock(
- register ExtendedVBLTaskPtr extendedVBLTaskPtr
- )
- {
- SCSIBusInquiryPB scsiBusInquiryPB;
- OSErr status;
-
- CLEAR(scsiBusInquiryPB);
- scsiBusInquiryPB.scsiPBLength = sizeof scsiBusInquiryPB;
- scsiBusInquiryPB.scsiFunctionCode = SCSIBusInquiry;
- scsiBusInquiryPB.scsiDevice.bus = kSCSIBusID;
- scsiBusInquiryPB.scsiDevice.targetID = kSCSITargetID;
- scsiBusInquiryPB.scsiDevice.LUN = kSCSILUN;
- SCSIAction((SCSI_PB *) &scsiBusInquiryPB);
- status = scsiBusInquiryPB.scsiResult;
- if (status != noErr) {
- DisplayError(status, "\pSCSIBusInquiry failed");
- DisposePtr((Ptr) extendedVBLTaskPtr);
- ExitToShell();
- }
- VBL.scsiExecIOPBSize = scsiBusInquiryPB.scsiIOpbSize;
- VBL.scsiPBPtr = (SCSIExecIOPB *)
- NewPtrSysClear(VBL.scsiExecIOPBSize);
- if (VBL.scsiPBPtr == NULL) {
- DisplayError(MemError(), "\pNo memory for param. block");
- DisposePtr((Ptr) extendedVBLTaskPtr);
- ExitToShell();
- }
- }
-
-
-
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * Start the VBL task. If this returns, the application must exit by setting
- * VBL.quitNow and waiting for both "active" flags to be set false.
- */
- void
- InstallVBLTask(
- register ExtendedVBLTaskPtr extendedVBLTaskPtr
- )
- {
- OSErr status;
-
- VBL.vblActive = TRUE;
- status = VInstall((QElemPtr) extendedVBLTaskPtr);
- if (status != noErr) {
- DisplayError(MemError(), "\pCannot install VBL task");
- DisposePtr((Ptr) VBL.scsiPBPtr);
- DisposePtr((Ptr) extendedVBLTaskPtr);
- ExitToShell();
- }
- }
-
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * This is the VBL task that is entered on each vbl clock tick. (The first
- * call is delayed to allow the application to reach a stable state).
- * On entrance, it performs the following tasks:
- * 1. If VBL.quitNow is set, it removes the VBL task, sets VBL.vblActive
- * FALSE, and exits.
- * 2. if VBL.quitNow is FALSE, VBL.scsiActive is FALSE, and no error
- * status is pending, it posts the next SCSI Device Inquiry request
- * and continues at step 3.
- * 3. Normal continuation: set vblCount to restart the VBL and exit. Note
- * that this might not have started a new I/O request.
- */
- #ifdef __powerc
- pascal void
- MyVBLTask(
- VBLTaskPtr vblTaskPtr
- );
- #else
- pascal void
- MyVBLTask(void)
- {
- VBLTaskPtr vblTaskPtr = (VBLTaskPtr) GetA0();
- #endif
- register ExtendedVBLTaskPtr extendedVBLTaskPtr;
- long oldA5;
- OSErr status;
-
- extendedVBLTaskPtr = (ExtendedVBLTaskPtr) vblTaskPtr;
- oldA5 = SetA5(VBL.applicationA5);
- ++VBL.vblCount;
- if (VBL.quitNow) {
- /*
- * Shutdown needed.
- */
- VBL.vblActive = FALSE;
- status = VRemove((QElemPtr) vblTaskPtr);
- if (status != noErr)
- DebugStr("\pVRemove error"); /* Can't happen */
- }
- else {
- VBL.vblTask.vblCount = 1; /* Quickly return here */
- if (VBL.scsiActive)
- ++VBL.scsiWasBusy;
- if (VBL.scsiActive == FALSE /* SCSI op completed? */
- && VBL.scsiError == noErr) { /* Error state clear? */
- /*
- * No SCSI request is currently in progress and there is
- * no pending error condition. Post the next request.
- */
- VBL.scsiActive = TRUE;
- ++VBL.scsiRequestCount;
- InitializeSCSIParamBlock(extendedVBLTaskPtr);
- status = SCSIAction((SCSI_PB *) VBL.scsiPBPtr);
- /*
- * If we failed here, something is seriously wrong and the
- * SCSI parameter block was not accepted. In this case, the
- * completion routine will never be called, so we must
- * signal an error here. Note that we started the VBL
- * timer: only setting VBL.quitNow stops the VBL task.
- */
- if (status != noErr) {
- VBL.scsiActive = FALSE;
- ++VBL.scsiCompleteCount;
- if (VBL.scsiError == noErr)
- VBL.scsiError = status;
- }
- }
- }
- (void) SetA5(oldA5);
- }
-
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * Setup the parameter block for the user's request. We do this each
- * time through the loop to make sure that the parameter block is
- * always consistent.
- */
- void
- InitializeSCSIParamBlock(
- register ExtendedVBLTaskPtr extendedVBLTaskPtr
- )
- {
- CLEAR(SCSI);
- SCSI.scsiPBLength = VBL.scsiExecIOPBSize;
- SCSI.scsiCompletion = (CallbackProc) MySCSICallbackProc;
- SCSI.scsiFunctionCode = SCSIExecIO;
- SCSI.scsiTimeout = kCompletionTimeout;
- SCSI.scsiDevice.bus = kSCSIBusID;
- SCSI.scsiDevice.targetID = kSCSITargetID;
- SCSI.scsiDevice.LUN = kSCSILUN;
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * Specify the transfer direction, if any, and set the other SCSI
- * operation flags. scsiSIMQNoFreeze prevents the SCSI Manager from
- * blocking further operation if an error is detected.
- */
- SCSI.scsiFlags =
- scsiSIMQNoFreeze /* Don't stop on errors */
- | scsiDontDisconnect; /* No disconnect */
- switch (VBL.commandSwitch) {
- case kIssueDeviceInquiry:
- SCSI.scsiCDB.cdbBytes[0] = kScsiCmdInquiry;
- SCSI.scsiCDB.cdbBytes[4] = sizeof (SCSI_Inquiry_Data);
- SCSI.scsiFlags |= scsiDirectionIn;
- SCSI.scsiTransferType = scsiTransferPolled;
- SCSI.scsiDataPtr = (unsigned char *) &VBL.inquiryData;
- SCSI.scsiDataLength = sizeof VBL.inquiryData;
- SCSI.scsiDataType = scsiDataBuffer;
- break;
- case kIssueTestUnitReady:
- SCSI.scsiCDB.cdbBytes[0] = kScsiCmdTestUnitReady;
- SCSI.scsiFlags |= scsiDirectionNone;
- break;
- }
- SCSI.scsiCDBLength = sizeof (SCSI_6_Byte_Command);
- SCSI.scsiSensePtr = (unsigned char *) &VBL.senseData;
- SCSI.scsiSenseLength = sizeof VBL.senseData;
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * Turn off select with attention so the device doesn't disconnect.
- * This is inefficient for most SCSI operations, but is reasonable
- * the Device Identity, Mode Sense, Test Unit Ready, and some other
- * other administrative commands.
- */
- SCSI.scsiIOFlags |= scsiDisableSelectWAtn;
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * Establish a linkage between the SCSI parameter block and the VBL.
- * This lets us access the VBL structure from the SCSI completion
- * procedure. Note that we could have done this using A5 and
- * global information, but this method translates to what drivers
- * must do in a clean manner.
- */
- SCSI.scsiDriverStorage = (unsigned char *) extendedVBLTaskPtr;
- }
-
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * This completion routine is called by the SCSI Manager when the
- * SCSIAction procedure completes.
- */
- pascal void
- MySCSICallbackProc(
- SCSIExecIOPB *scsiExecIOPBPtr
- )
- {
- register ExtendedVBLTaskPtr extendedVBLTaskPtr;
- long oldA5;
- register SCSI_Sense_Data *senseDataPtr;
- #define SENSE (*senseDataPtr)
-
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * Recover the extended VBL task pointer and, through it, the
- * application A5 value.
- */
- extendedVBLTaskPtr =
- (ExtendedVBLTaskPtr) scsiExecIOPBPtr->scsiDriverStorage;
- oldA5 = SetA5(VBL.applicationA5);
- VBL.scsiActive = FALSE;
- ++VBL.scsiCompleteCount;
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * Check for errors and set the device state accordingly:
- * noErr -> kDeviceReady if Test Unit Ready
- * scsiDeviceNotThere -> kDeviceNotPresent
- * scsiBusy -> Do nothing: VBL will retry us.
- * scsiNonZeroStatus -> (check autosense result)
- * other errors -> Set VBL.scsiError.
- */
- if (SCSI.scsiResult == scsiDataRunError
- && SCSI.scsiDataResidual >= 0)
- SCSI.scsiResult = 0;
- switch (SCSI.scsiResult) {
- case noErr:
- if (VBL.commandSwitch == kIssueTestUnitReady)
- VBL.deviceState = kDeviceReady;
- VBL.commandSwitch = 1 - VBL.commandSwitch; /* Normal */
- break;
- case scsiSelectTimeout:
- case scsiDeviceNotThere:
- VBL.deviceState = kDeviceNotPresent;
- break;
- case scsiBusy: /* SCSI Subsystem busy */
- break; /* Re-issue this command */
- case scsiNonZeroStatus:
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * Look at the error code in the autosense buffer.
- */
- senseDataPtr =
- (SCSI_Sense_Data *) scsiExecIOPBPtr->scsiSensePtr;
- if ((SENSE.errorCode & kScsiSenseInfoMask)
- != kScsiSenseInfoValid) {
- if (VBL.scsiError == noErr)
- VBL.scsiError = scsiNonZeroStatus; /* Bad sense */
- }
- else {
- switch (SENSE.senseKey & kScsiSenseKeyMask) {
- case kScsiSenseNotReady: /* Expected */
- /*
- * In a real application, you would also notice
- * whether the "operator intervention needed" code
- * was present in the additionalSenseCode byte.
- * This indicates "no device inserted" (usually).
- * We should have done a TestUnitReady. In any case,
- * continue here until the sense key changes.
- */
- VBL.deviceState = kDeviceNotReady;
- break;
- case kScsiSenseNone: /* No error */
- VBL.commandSwitch = 1 - VBL.commandSwitch;
- break;
- case kScsiSenseRecoveredErr: /* Retry warning */
- case kScsiSenseUnitAtn: /* Unit reset */
- break; /* Just retry these */
- default: /* Something is */
- goto seriousError; /* very incorrect */
- } /* Which sense key */
- } /* If ok sense key */
- break; /* If sense error */
- default:
- seriousError:
- if (VBL.scsiError == noErr)
- VBL.scsiError = SCSI.scsiResult; /* Real trouble */
- break;
- }
- (void) SetA5(oldA5);
- }
-